﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;

namespace LoadGenerator
{
    internal interface Behavior
    {
        int nextLoad();
    }

    internal class Linear : Behavior
    {
        int _load;
        int _lastLoad;
        int _times;
        double _alpha;
        BehaviorFactory.behaviortype _behave; 

        public Linear(int load, double alpha, BehaviorFactory.behaviortype b)
        {
            _load = load;
            _lastLoad = load;
            _alpha = alpha;
            _behave = b;
            _times = 0;
        }

        public int nextLoad()
        {
            int load = 0;
            switch(_behave)
            {
                case BehaviorFactory.behaviortype.constant:
                    load = _load;
                    break;
                case BehaviorFactory.behaviortype.decreasing:
                    load = (int)Math.Ceiling(_load - (_times * _alpha));
                    break;
                case BehaviorFactory.behaviortype.increasing:
                    load = (int)Math.Ceiling(_load + (_times * _alpha));
                    break;
            }
            _lastLoad = load;
            _times++;
            return load;
        }
    }

    internal class Exponential : Behavior
    {
        int _load;
        int _lastLoad;
        int _times;
        double _alpha;
        BehaviorFactory.behaviortype _behave;

        public Exponential(int load, double alpha, BehaviorFactory.behaviortype b)
        {
            _load = load;
            _lastLoad = load;
            _alpha = alpha;
            _behave = b;
            _times = 0;
        }

        public int nextLoad()
        {
            int load = 0;
            switch (_behave)
            {
                case BehaviorFactory.behaviortype.constant:
                    load = _load;
                    break;
                case BehaviorFactory.behaviortype.decreasing:
                    load = (int)Math.Ceiling(_lastLoad - (Math.Exp(_alpha * _times)));
                    break;
                case BehaviorFactory.behaviortype.increasing:
                    load = (int)Math.Ceiling(_lastLoad + (Math.Exp(_alpha * _times)));
                    break;
            }
            _lastLoad = load;
            _times++;
            return load;
        }
    }

    internal class BehaviorFactory
    {
        internal enum type { linear, exponential }
        internal enum behaviortype { constant, increasing, decreasing }

        internal BehaviorFactory()
        {

        }

        internal static Behavior getBehavior(String val, String load, String alpha)
        {
            type t;
            behaviortype b;
            String[] vals = val.Split(new char[]{'-'});
            if (vals.Length > 0)
            {
                if (vals[0].Equals("linear")) t = type.linear;
                else if (vals[0].Equals("exponential")) t = type.exponential;
                else throw new Exception("Unrecognized type." + vals[0]);
            }
            else
                throw new Exception("Unrecognized type." + val);

            if (vals.Length > 1)
            {
                if (vals[1].Equals("constant")) b = behaviortype.constant;
                else if (vals[1].Equals("increasing")) b = behaviortype.increasing;
                else if (vals[1].Equals("decreasing")) b = behaviortype.decreasing;
                else throw new Exception("Unrecognized type." + vals[1]);
            }
            else
                throw new Exception("Unrecognized type." + val);

            int l = int.Parse(load);
            double a = double.Parse(alpha);
            switch (t)
            {
                case type.exponential:
                    return new Exponential(l, a, b);
                case type.linear:
                default:
                    return new Linear(l, a, b);
            }
        }
    }

    public class Pattern
    {
        DateTime _start;
        DateTime _end;
        Behavior _behavior;

        internal Pattern(String line)
        {
            parse(line);
        }

        private void parse(String line)
        {
            String[] vals = line.Split(new char[] {';'});
            int cnt = 0;
            if (vals.Length > cnt)
                _start = DateTime.Parse(vals[cnt]);
            cnt++;
            if (vals.Length > cnt)
                _end = DateTime.Parse(vals[cnt]);
            cnt++;
            if (vals.Length > (cnt + 2))
            {
                _behavior = BehaviorFactory.getBehavior(vals[cnt], vals[cnt + 1],
                    vals[cnt + 2]);
            }
        }

        public bool active 
        { 
            get 
            {
                DateTime time = DateTime.Now;
                if ((time.CompareTo(_start) > 0) && (time.CompareTo(_end) <= 0))
                    return true;

                return false;
            } 
        }

        public int nextload { get { return _behavior.nextLoad(); } }
    }

    public class LoadConfiguration
    {
        IList _patterns;

        public LoadConfiguration(String file)
        {
            _patterns = new ArrayList();
            StreamReader reader = new StreamReader(file);
            while (reader.Peek() > 0)
            {
                String line = reader.ReadLine();
                Pattern pat = new Pattern(line);
                _patterns.Add(pat);
            }
        }

        public int load
        {
            get
            {
                foreach (Pattern p in _patterns)
                {
                    if (p.active)
                        return p.nextload;
                }

                return 0;
            }
        }
    }
}
